﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace PSLib.CodeFormating.Lexems
{
	/// <summary>
	/// Class for lexem processing.
	/// </summary>
	public static class LexemParser
	{
		#region Public methods

		/// <summary>
		/// Parse input string and create lexem collection
		/// </summary>
		/// <param name="source">Source string.</param>
		/// <param name="endsWithBreakLine">Indicates breakline lexem trimming.</param>
		/// <returns>Lexem collection</returns>
		public static LexemList Parse(string source, bool endsWithBreakLine)
		{
			return Parse(source, null, endsWithBreakLine);
		}
		/// <summary>
		/// Parse input string and create lexem collection
		/// </summary>
		/// <param name="source">Source string.</param>
		/// <param name="adapter">Language adapter.</param>
		/// <param name="endsWithBreakLine">Indicates breakline lexem trimming.</param>
		/// <returns>Lexem collection</returns>
		public static LexemList Parse(string source, Adapters.LanguageAdapter adapter, bool endsWithBreakLine)
		{
			LexemList lexemCollection = new LexemList();
			//if (string.IsNullOrEmpty(source)) throw new ArgumentException("Source string must be not empty");
			//Clear current lexems
			lexemCollection.Clear();
			//
			if (!string.IsNullOrEmpty(source))
			{
				StringReader strReader = new StringReader(source);

				string line = string.Empty;
				//Get first line
				line = strReader.ReadLine();
				//While we has lines parse lexems
				while (line != null)
				{
					while (!string.IsNullOrEmpty(line))
					{
						//Getting lexems
						Lexem lexem = GetLexem(line);
						//Determine lexem type
						lexem.Type = DetermineType(lexem.LexemSource, adapter);
						//Cut line
						line = line.Substring(lexem.LexemSource.Length);
						lexemCollection.Add(lexem);
					}
					//Adding break line lexem
					lexemCollection.Add(new Lexem { LexemSource = "\n", Type = LexemType.BreakLine });
					//Getting next line
					line = strReader.ReadLine();
				}
			}
			if (adapter != null) lexemCollection = adapter.PostProcessLexemList(lexemCollection);
			if (!endsWithBreakLine) lexemCollection.RemoveAt(lexemCollection.Count - 1);
			return lexemCollection;
		}

		/// <summary>
		/// Optimize lexem repetition.
		/// </summary>
		/// <param name="lexems">Source lexem list.</param>
		/// <returns></returns>
		public static LexemList Optimize(LexemList lexemList,bool optimizeSpaces)
		{
			if (lexemList == null) throw new ArgumentException("Lexem list must exists");
			if (lexemList.Count == 0) return new LexemList(lexemList.ToArray());
			LexemList result = new LexemList();

			Lexem lastLexem = null;
			foreach (var item in lexemList)
			{
				if (lastLexem != null)
				{
					if (optimizeSpaces && 
						lastLexem.Type == LexemType.Space && 
						item.Type != LexemType.Space) lastLexem.Type = item.Type;

					if (lastLexem.Type != item.Type)
					{
						result.Add(lastLexem.Clone());
						lastLexem = item.Clone();
					}
					else lastLexem.LexemSource += item.LexemSource;
				}
				else lastLexem = item.Clone();
			}
			if (lastLexem != null) result.Add(lastLexem.Clone());
			return result;
		}

		#endregion

		#region Private methods

		private static LexemType DetermineType(string lexem, Adapters.LanguageAdapter adapter)
		{
			LexemType type = LexemType.None;
			if (!string.IsNullOrEmpty(lexem))
			{
				int outDigit = 0;
				if (Int32.TryParse(lexem, out outDigit)) type = LexemType.Digit;
				else if (lexem.StartsWith("'") || lexem.StartsWith("\"")) type = LexemType.String;
				else
				{
					foreach (var item in lexem)
					{
						if (char.IsPunctuation(item)) type = LexemType.Punctuation;
						if (char.IsSymbol(item)) type = LexemType.Symbol;
						if (char.IsWhiteSpace(item)) type = LexemType.Space;
						if (type != LexemType.None) break;
					}

					if (type == LexemType.None)
					{
						if (adapter != null) type = adapter.ProcessLexem(lexem);
						if(type == LexemType.None) type = LexemType.Word;
					}
				}
			}
			return type;
		}

		private static Lexem GetLexem(string source)
		{
			Lexem result = new Lexem();

			string word = string.Empty;
			for (int i = 0; i < source.Length; i++)
			{
				char c = source[i];
				if (char.IsSeparator(c) || char.IsPunctuation(c) || char.IsSymbol(c) || char.IsControl(c))
				{
					if (string.IsNullOrEmpty(word))
					{
						if (c == '"' || c == '\'') word = GetLogicString(source, i, c);
						else word += c;
					}
					break;
				}
				word += c;
			}

			result.LexemSource = word;

			return result;
		}

		private static string GetLogicString(string source, int startPos, char stringStartSymbol)
		{
			string result = string.Empty;
			int endPos = source.IndexOf(stringStartSymbol, startPos + 1);
			if (endPos == -1) endPos = source.Length;
			int aditionalSymbol = (endPos == source.Length) ? 0:1;
			result = source.Substring(startPos, endPos - startPos + aditionalSymbol);
			return result;
		}

		#endregion
	}
}
